﻿using System;
using Roslyn.Compilers.CSharp;
using System.IO;

namespace TranslateCS
{
    class Program
    {
        static void Main(string[] args)
        {
            var ast = SyntaxTree.ParseText(File.ReadAllText("VolTarget.cs"));
            var root = ast.GetRoot();
            using(var output = new StreamWriter("VolTargetConverted.cpp"))
                root.Accept(new CppWriter(output));
        }
    }

    class CppWriter : Roslyn.Compilers.CSharp.SyntaxWalker
    {
        private int _depth=0;
        private TextWriter _output;
        private bool _debug = false;

        public CppWriter(TextWriter t) : base(Roslyn.Compilers.Common.SyntaxWalkerDepth.Token) 
        { 
            _output = t;
            _output.WriteLine("#include \"MCPayoff.h\"");
            _output.WriteLine();
        }

        public override void Visit(SyntaxNode node)
        {
            //track depth
            _depth++;
            if(_debug)
                Console.WriteLine("{0}{1}{2} {3}", _depth, new string(' ', 1 + _depth * 2), node.Kind, node.GetType());
            base.Visit(node);
            _depth--;
        }

        public override void VisitToken(SyntaxToken token)
        {
            if (_debug)
                Console.WriteLine(" {0}{1}\t\"{2}\"", new string(' ', 1 + _depth * 2), token.Kind, token.ToString());
            _output.Write(token.ToFullString());
            base.VisitToken(token);
        }

        public override void VisitUsingDirective(UsingDirectiveSyntax node) {} //remove all "using Xxx;"

        public override void VisitClassDeclaration(ClassDeclarationSyntax node)
        {
            //convert
            //  public class userFunctions : EQMCBase
            //to
            //  class userFunctions : public EQMCBase
            base.VisitClassDeclaration(
                node.WithModifiers(Syntax.TokenList()) //remove "public" before class
                .WithBaseList(node.BaseList.WithTypes(Syntax.SeparatedList<TypeSyntax>(
                    node.BaseList.Types.First()
                    .WithLeadingTrivia(Syntax.DisabledText("public "))))) //add "public" before base class
                .WithOpenBraceToken(node.OpenBraceToken.WithTrailingTrivia(Syntax.CarriageReturnLineFeed, Syntax.DisabledText("public:"), Syntax.CarriageReturnLineFeed)) //add "public:" within class decl
                .WithTrailingTrivia(Syntax.DisabledText(";"), Syntax.CarriageReturnLineFeed)
            ); 
        }

        public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
        {
            base.VisitMethodDeclaration(
                node.WithModifiers(Syntax.TokenList()) //remove "public" before methods
                .WithParameterList(node.ParameterList.WithTrailingTrivia(Syntax.DisabledText(" const"), Syntax.CarriageReturnLineFeed))
                );
        }

        public override void VisitVariableDeclaration(VariableDeclarationSyntax node)
        {
            //convert
            //  double[] weights, bsk, yearFractions;
            //to
            //  vector<double> weights, bsk, yearFractions;
            if(node.Type is ArrayTypeSyntax)
                base.VisitVariableDeclaration(node.WithType(Syntax.IdentifierName("    vector<double> ")));
            else
                base.VisitVariableDeclaration(node);
        }

        public override void VisitArrayCreationExpression(ArrayCreationExpressionSyntax node)
        {
            base.VisitArrayCreationExpression(node);
        }

        public override void VisitExpressionStatement(ExpressionStatementSyntax node)
        {
            if (node.Expression is BinaryExpressionSyntax)
            {
                var bnode = node.Expression as BinaryExpressionSyntax;
                if(bnode.Right is ArrayCreationExpressionSyntax)
                {
                    //convert
                    //  weights = new double[nReturns]; 
                    //to
                    //  weights.resize(nReturns);
                    var arrayCreationNode = bnode.Right as ArrayCreationExpressionSyntax;
                    var size = arrayCreationNode.Type.RankSpecifiers[0].Sizes[0];
                    base.Visit(
                        Syntax.ExpressionStatement(
                            Syntax.InvocationExpression(
                                Syntax.MemberAccessExpression(
                                    SyntaxKind.MemberAccessExpression,
                                    bnode.Left,
                                    Syntax.IdentifierName("resize")
                                ),
                                Syntax.ArgumentList(Syntax.SeparatedList<ArgumentSyntax>(Syntax.Argument(size)))
                            )
                        ).WithTrailingTrivia(Syntax.CarriageReturnLineFeed)
                    );
                    return;
                }
            }
            base.VisitExpressionStatement(node);
        }
    }
}
